package com.my.project.http.client.util;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.json.JsonWriteFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;
import java.util.Map;

public class JsonUtils {

    public static final ObjectMapper mapper = new ObjectMapper();

    static {
        // to prevent exception when encountering unknown property:
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // JsonParser.Feature for configuring parsing settings:
        // to allow C/C++ style comments in JSON (non-standard, disabled by default)
        // (note: with Jackson 2.5, there is also `mapper.enable(feature)` / `mapper.disable(feature)`)
        mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
        // to allow (non-standard) unquoted field names in JSON:
        mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        // to allow use of apostrophes (single quotes), non standard
        mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
        mapper.setConfig(deserializationConfig
            .with(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
            .with(JsonReadFeature.ALLOW_TRAILING_COMMA)
            .with(JsonReadFeature.ALLOW_MISSING_VALUES)
            .with(JsonWriteFeature.ESCAPE_NON_ASCII));
    }

    public static String toJsonString(Object o) {
        return toJsonString(o, false);
    }

    public static String toJsonString(Object o, boolean pretty) {
        try {
            if (pretty) {
                return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(o);
            } else {
                return mapper.writeValueAsString(o);
            }
        } catch (Exception e) {
            throw new RuntimeException("object to json string error", e);
        }
    }

    public static <T> T jsonToObject(String json, Class<T> clazz) {
        try {
            return mapper.readValue(json, clazz);
        } catch (Exception e) {
            throw new RuntimeException("json string to object error", e);
        }
    }

    public static <T> T jsonToObject(String json, TypeReference<T> valueTypeRef) {
        try {
            return mapper.readValue(json, valueTypeRef);
        } catch (Exception e) {
            throw new RuntimeException("json string to object error", e);
        }
    }

    public static <K, V> Map<K, V> jsonToMap(String json, Class<K> keyClazz, Class<V> valueClazz) {
        try {
            return mapper.readValue(json, new TypeReference<Map<K, V>>() {
            });
        } catch (Exception e) {
            throw new RuntimeException("json string to map error", e);
        }
    }

    public static <T> List<T> jsonToList(String json, Class<T> clazz) {
        try {
            return mapper.readValue(json, new TypeReference<List<T>>() {
            });
        } catch (Exception e) {
            throw new RuntimeException("json string to list error", e);
        }
    }
}
